/*
  CoveWhsFs
  Copyright (C) 2011 Alex Piskarev  
  http://code.google.com/p/covewhsfs/
  
  Original work:
  Dokan
  Copyright (C) 2010 Hiroki Asakawa info@dokan-dev.net
  http://dokan-dev.net/en

This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the Free
Software Foundation; either version 3 of the License, or (at your option) any
later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along
with this program. If not, see <http://www.gnu.org/licenses/>.
*/


#include "sys.h"

#ifdef ALLOC_PRAGMA
#pragma alloc_text (PAGE, DokanDispatchCreate)
#endif


// We must NOT call without VCB lcok
PDokanFCB
DokanAllocateFCB(
	__in PDokanVCB Vcb
	)
{
	PDokanFCB fcb = ExAllocatePool(sizeof(DokanFCB));

	if (fcb == NULL) {
		return NULL;
	}

	ASSERT(fcb != NULL);
	ASSERT(Vcb != NULL);

	RtlZeroMemory(fcb, sizeof(DokanFCB));

	fcb->Identifier.Type = FCB;
	fcb->Identifier.Size = sizeof(DokanFCB);

	fcb->Vcb = Vcb;

	ExInitializeResourceLite(&fcb->MainResource);
	ExInitializeResourceLite(&fcb->PagingIoResource);

	ExInitializeFastMutex(&fcb->AdvancedFCBHeaderMutex);

#if _WIN32_WINNT >= 0x0501
	FsRtlSetupAdvancedHeader(&fcb->AdvancedFCBHeader, &fcb->AdvancedFCBHeaderMutex);
#else
	if (DokanFsRtlTeardownPerStreamContexts) {
		FsRtlSetupAdvancedHeader(&fcb->AdvancedFCBHeader, &fcb->AdvancedFCBHeaderMutex);
	}
#endif


	fcb->AdvancedFCBHeader.ValidDataLength.LowPart = 0xffffffff;
	fcb->AdvancedFCBHeader.ValidDataLength.HighPart = 0x7fffffff;

	fcb->AdvancedFCBHeader.Resource = &fcb->MainResource;
	fcb->AdvancedFCBHeader.PagingIoResource = &fcb->PagingIoResource;

	fcb->AdvancedFCBHeader.AllocationSize.QuadPart = 4096;
	fcb->AdvancedFCBHeader.FileSize.QuadPart = 4096;

    // Make the I/O manager ask if Fast I/O is possible
    //fcb->AdvancedFCBHeader.IsFastIoPossible = FastIoIsQuestionable;
	fcb->AdvancedFCBHeader.IsFastIoPossible = FastIoIsNotPossible;

	ExInitializeResourceLite(&fcb->Resource);

	InitializeListHead(&fcb->NextCCB);
	InsertTailList(&Vcb->NextFCB, &fcb->NextFCB);

	InterlockedIncrement(&Vcb->FcbAllocated);

	return fcb;
}


PDokanFCB
DokanGetFCB(
	__in PDokanVCB	        Vcb,
	__in UNICODE_STRING		FileName)
{
	PLIST_ENTRY		thisEntry, nextEntry, listHead;
	PDokanFCB		fcb = NULL;
	ULONG			pos;

	KeEnterCriticalRegion();
	ExAcquireResourceExclusiveLite(&Vcb->Resource, TRUE);

	// search the FCB which is already allocated
	// (being used now)
	listHead = &Vcb->NextFCB;

    for (thisEntry = listHead->Flink;
			thisEntry != listHead;
			thisEntry = nextEntry) {

		nextEntry = thisEntry->Flink;

        fcb = CONTAINING_RECORD(thisEntry, DokanFCB, NextFCB);

		if (fcb->FileName.Length == FileName.Length) {
			// FileName.Length is in bytes
			for (pos = 0; pos < FileName.Length / sizeof(WCHAR); ++pos) {
				if (fcb->FileName.Buffer[pos] != FileName.Buffer[pos])
					break;
			}
			// we have the FCB which is already allocated and used
			if (pos == FileName.Length / sizeof(WCHAR))
				break;
		}

		fcb = NULL;
	}

	// we don't have FCB
	if (fcb == NULL) {
		DDbgPrint("  Allocate FCB\n");
		
		fcb = DokanAllocateFCB(Vcb);
		
		// no memory?
		if (fcb == NULL) {
			ExFreePool(FileName.Buffer);
			ExReleaseResourceLite(&Vcb->Resource);
			KeLeaveCriticalRegion();
			return NULL;
		}

		ASSERT(fcb != NULL);

		fcb->FileName.Buffer = FileName.Buffer;
		fcb->FileName.Length = FileName.Length;
		fcb->FileName.MaximumLength = FileName.MaximumLength;
        
        PrintIdType(fcb);
        
	// we already have FCB
	} else {
		// FileName (argument) is never used and must be freed
		ExFreePool(FileName.Buffer);
	}

	ExReleaseResourceLite(&Vcb->Resource);
	KeLeaveCriticalRegion();

	InterlockedIncrement(&fcb->FileCount);
	return fcb;
}



NTSTATUS
DokanFreeFCB(
  __in PDokanFCB Fcb
  )
{
	PDokanVCB vcb;

	ASSERT(Fcb != NULL);

	vcb = Fcb->Vcb;

	KeEnterCriticalRegion();
	ExAcquireResourceExclusiveLite(&vcb->Resource, TRUE);
	ExAcquireResourceExclusiveLite(&Fcb->Resource, TRUE);

	Fcb->FileCount--;

	if (Fcb->FileCount == 0) {

		RemoveEntryList(&Fcb->NextFCB);

		DDbgPrint("  Free FCB:%X\n", Fcb);
		ExFreePool(Fcb->FileName.Buffer);

#if _WIN32_WINNT >= 0x0501
		FsRtlTeardownPerStreamContexts(&Fcb->AdvancedFCBHeader);
#else
		if (DokanFsRtlTeardownPerStreamContexts) {
			DokanFsRtlTeardownPerStreamContexts(&Fcb->AdvancedFCBHeader);
		}
#endif
		ExReleaseResourceLite(&Fcb->Resource);

		ExDeleteResourceLite(&Fcb->Resource);
		ExDeleteResourceLite(&Fcb->MainResource);
		ExDeleteResourceLite(&Fcb->PagingIoResource);

		InterlockedIncrement(&vcb->FcbFreed);
		ExFreePool(Fcb);

	} else {
		ExReleaseResourceLite(&Fcb->Resource);
	}

	ExReleaseResourceLite(&vcb->Resource);
	KeLeaveCriticalRegion();

	return STATUS_SUCCESS;
}



PDokanCCB
DokanAllocateCCB(
	__in PDokanDCB	Dcb,
	__in PDokanFCB	Fcb
	)
{
	PDokanCCB ccb = ExAllocatePool(sizeof(DokanCCB));

	if (ccb == NULL)
		return NULL;

	ASSERT(ccb != NULL);
	ASSERT(Fcb != NULL);

	RtlZeroMemory(ccb, sizeof(DokanCCB));

	ccb->Identifier.Type = CCB;
	ccb->Identifier.Size = sizeof(DokanCCB);

	ccb->Fcb = Fcb;

	ExInitializeResourceLite(&ccb->Resource);

	InitializeListHead(&ccb->NextCCB);

	KeEnterCriticalRegion();
	ExAcquireResourceExclusiveLite(&Fcb->Resource, TRUE);

	InsertTailList(&Fcb->NextCCB, &ccb->NextCCB);

	ExReleaseResourceLite(&Fcb->Resource);
	KeLeaveCriticalRegion();

	ccb->MountId = Dcb->MountId;

    InitializeListHead(&ccb->OpenContext);
    
	InterlockedIncrement(&Fcb->Vcb->CcbAllocated);
    
    DDbgPrint("  CCB Allocated: %08lx\n", ccb);
	return ccb;
}



NTSTATUS
DokanFreeCCB(
  __in PDokanCCB ccb
  )
{
	PDokanFCB fcb;
    PLIST_ENTRY OpenContextEntry;

	ASSERT(ccb != NULL);
	
	fcb = ccb->Fcb;

	KeEnterCriticalRegion();
	ExAcquireResourceExclusiveLite(&fcb->Resource, TRUE);

	RemoveEntryList(&ccb->NextCCB);

	ExReleaseResourceLite(&fcb->Resource);
	KeLeaveCriticalRegion();

	ExDeleteResourceLite(&ccb->Resource);

	if (ccb->SearchPattern) {
		ExFreePool(ccb->SearchPattern);
	}

    while (!IsListEmpty(&ccb->OpenContext))
    {
        // Free the open context list
        OpenContextEntry = RemoveHeadList(&ccb->OpenContext);
        ExFreePool(OpenContextEntry);
    }
    
	ExFreePool(ccb);
	InterlockedIncrement(&fcb->Vcb->CcbFreed);

	return STATUS_SUCCESS;
}


LONG
DokanUnicodeStringChar(
	__in PUNICODE_STRING UnicodeString,
	__in WCHAR	Char)
{
	ULONG i = 0;
	for (; i < UnicodeString->Length/sizeof(WCHAR); ++i) {
		if (UnicodeString->Buffer[i] == Char) {
			return i;
		}
	}
	return -1;
}


VOID
SetFileObjectForVCB(
	__in PFILE_OBJECT	FileObject,
	__in PDokanVCB		Vcb)
{
	FileObject->SectionObjectPointer = &Vcb->SectionObjectPointers;
	FileObject->FsContext = &Vcb->VolumeFileHeader;
}

///
/// Retrieves the actual full file name used to open a file
///
UNICODE_STRING
DokanCreateFileFullName(
    __in UNICODE_STRING FileName,
    __in const UNICODE_STRING RelatedFileFullName)
{
    // Return value
    UNICODE_STRING R;
    SIZE_T RSize;
    
    // Check for trailing backslash and remove
    if ((FileName.Length > sizeof(WCHAR)) &&
        (FileName.Buffer[FileName.Length/sizeof(WCHAR)-1] == L'\\')) 
    {
        FileName.Length -= sizeof(WCHAR);
    }
    
    // Allocate
    RSize = FileName.Length + sizeof(WCHAR) + RelatedFileFullName.Length + sizeof(WCHAR);
    if (RSize > 0xFFFF) // USHORT.Max
    {
        // Too large!
        R.Buffer = NULL;
        R.MaximumLength = 0;
        R.Length = 0;
        return R;
    }
    
    R.Buffer = ExAllocatePool(RSize);
    if (R.Buffer == NULL)
    {
        // Not enough resources
        R.Buffer = NULL;
        R.MaximumLength = 0;
        R.Length = 0;
        return R;
    }
    
    RtlZeroMemory(R.Buffer, RSize);
    
    R.MaximumLength = (USHORT)RSize;
    R.Length = 0;
    
    // Concatenate
    if (RelatedFileFullName.Length > 0)
    {
        RtlCopyMemory(R.Buffer, RelatedFileFullName.Buffer, RelatedFileFullName.Length);
        R.Length += RelatedFileFullName.Length;
        
        //RtlUnicodeStringCopy(&R, &RelatedFileFullName);
        RtlUnicodeStringCatString(&R, L"\\");
        RtlUnicodeStringCat(&R, (PCUNICODE_STRING)&FileName);
    }
    else
    {
        RtlUnicodeStringCopy(&R, (PCUNICODE_STRING)&FileName);
    }

    // Remove trailing slash, if present
    if (R.Length > sizeof(WCHAR) &&
       R.Buffer[R.Length/sizeof(WCHAR)-1] == L'\\') {
        R.Length -= sizeof(WCHAR);
    }
    
    return R;
}

UNICODE_STRING
DokanGetRelatedFileFullName(
    __in PFILE_OBJECT FileObject)
{
    PFILE_OBJECT RelatedFileObject = FileObject->RelatedFileObject;
    PDokanCCB    RelatedCCB = NULL;
    PDokanFCB    RelatedFCB = NULL;
    
    if (RelatedFileObject != NULL) 
    {
        // There is a related file object
        RelatedCCB = (PDokanCCB)RelatedFileObject->FsContext2;
        if (RelatedCCB != NULL)
        {
            RelatedFCB = RelatedCCB->Fcb;
        }
        
        // Validate related FCB
        DDbgPrint("  related FCB: %08lx\n", RelatedFCB);
        
        if (RelatedFCB != NULL)
        {
            PrintIdType(RelatedFCB);
            if (GetIdentifierType(RelatedFCB) != FCB)
            {
                UNICODE_STRING blank;
                
                // Not an FCB!
                DDbgPrint("  related FCB is not a valid FCB\n");
                
                blank.Length = 0;
                blank.MaximumLength = 0;
                blank.Buffer = NULL;
                
                //return RelatedFileObject->FileName;
                return blank;
            }
            
            DDbgPrint("RelatedFCB->FileName.Length: %d\n", RelatedFCB->FileName.Length);
            DDbgPrint("RelatedFileObject->FileName.Length: %d\n", RelatedFileObject->FileName.Length);
            
            // We processed this file object
            // The related FCB file name is a combination of the file name of that 
            // file object and the FCB file name of its parent
            return RelatedFCB->FileName;

        }
        else
        {
            UNICODE_STRING blank;
            DDbgPrint("  no related FCB\n");
                
            blank.Length = 0;
            blank.MaximumLength = 0;
            blank.Buffer = NULL;
            
            return blank;
        }

    }
    else
    {
        UNICODE_STRING blank;
        blank.Length = 0;
        blank.MaximumLength = 0;
        blank.Buffer = NULL;
        
        return blank;
    }
    
}

BOOLEAN
DokanIsRootOpen(
    __in UNICODE_STRING FileName,
    __in UNICODE_STRING RelatedFileFullName)
{
    if (RelatedFileFullName.Length == 0 &&
        (FileName.Length == sizeof(WCHAR)) &&
        (FileName.Buffer[0] == L'\\')) 
    {
        return TRUE;
    }
    else
    {
        return FALSE;
    }
}

BOOLEAN
DokanCheckAndAdjustFileName(
    __in PUNICODE_STRING FileName,
    __in const UNICODE_STRING RelatedFileFullName)
{
    //
    //  The Win32 layer sends double beginning backslashes, fix.
    //  Also, check for \\\\
    //

    if ((FileName->Length > sizeof(WCHAR)) &&
        (FileName->Buffer[1] == L'\\') &&
        (FileName->Buffer[0] == L'\\')) {

        FileName->Length -= sizeof(WCHAR);

        RtlMoveMemory( &FileName->Buffer[0],
                       &FileName->Buffer[1],
                       FileName->Length );

        //
        //  If there are still two beginning backslashes, the name is bogus.
        //

        if ((FileName->Length > sizeof(WCHAR)) &&
            (FileName->Buffer[1] == L'\\') &&
            (FileName->Buffer[0] == L'\\')) {

            return FALSE;
        }
    }
    
    if (RelatedFileFullName.Length != 0 && 
        FileName->Length != 0 &&
        FileName->Buffer[0] == L'\\')
    {
        // Related file name implies relative path
        return FALSE;
    }
    // else
    //   An open with no related file name CAN be relative.
    //   In this case it is relative to the root.
    
    return TRUE;
    
}

NTSTATUS
DokanDispatchCreate(
	__in PDEVICE_OBJECT DeviceObject,
	__in PIRP Irp
	)

/*++

Routine Description:

	This device control dispatcher handles create & close IRPs.

Arguments:

	DeviceObject - Context for the activity.
	Irp 		 - The device control argument block.

Return Value:

	NTSTATUS

--*/
{
	PDokanVCB			Vcb;
	PDokanDCB			Dcb;
	PIO_STACK_LOCATION	IrpSp = NULL;

	PDokanFCB			Fcb;
	PDokanCCB			Ccb;
    
	NTSTATUS			Status = STATUS_INVALID_PARAMETER;
    ULONG				Info = 0;
    
	//PEPROCESS			process;
	//PUNICODE_STRING		processImageName;
    
	PEVENT_CONTEXT		EventContext;
	ULONG				EventLength;
    
	//HANDLE				accessTokenHandle;
    //BOOLEAN             acquiredVCB = FALSE;
    
    // Common values
    PFILE_OBJECT FileObject;
    PFILE_OBJECT RelatedFileObject;
    UNICODE_STRING FileName;
    ULONG AllocationSize;
    PFILE_FULL_EA_INFORMATION EaBuffer;
    PACCESS_MASK DesiredAccess;
    ULONG Options;
    UCHAR FileAttributes;
    USHORT ShareAccess;
    ULONG EaLength;
    
    // Decoded information
    UNICODE_STRING      RelatedFileFullName;
    UNICODE_STRING      FileFullName;
    
    // Flags
    BOOLEAN CreateDirectory;
    BOOLEAN SequentialOnly;
    BOOLEAN NoIntermediateBuffering;
    BOOLEAN OpenDirectory;
    BOOLEAN IsPagingFile;
    BOOLEAN OpenTargetDirectory;
    BOOLEAN DirectoryFile;
    BOOLEAN NonDirectoryFile;
    BOOLEAN NoEaKnowledge;
    BOOLEAN DeleteOnClose;
    BOOLEAN TemporaryFile;
    BOOLEAN FileNameOpenedDos = FALSE;
    
    ULONG CreateDisposition;
    
	PAGED_CODE();

    RelatedFileFullName.Length = 0;
    RelatedFileFullName.MaximumLength = 0;
    RelatedFileFullName.Buffer = NULL;
    
	__try {
		FsRtlEnterFileSystem();

		IrpSp = IoGetCurrentIrpStackLocation(Irp);
        
        DDbgPrintEnterRequest("DokanCreate", IrpSp);
        
        //
        //  Reference our input parameters to make things easier
        //

        ASSERT( IrpSp->Parameters.Create.SecurityContext != NULL );

        FileObject        = IrpSp->FileObject;
        FileName          = FileObject->FileName;
        RelatedFileObject = FileObject->RelatedFileObject;
        AllocationSize    = Irp->Overlay.AllocationSize.LowPart;
        EaBuffer          = Irp->AssociatedIrp.SystemBuffer;
        DesiredAccess     = &IrpSp->Parameters.Create.SecurityContext->DesiredAccess;
        Options           = IrpSp->Parameters.Create.Options;
        FileAttributes    = (UCHAR)(IrpSp->Parameters.Create.FileAttributes & ~FILE_ATTRIBUTE_NORMAL);
        ShareAccess       = IrpSp->Parameters.Create.ShareAccess;
        EaLength          = IrpSp->Parameters.Create.EaLength;
        
		if (FileObject == NULL) {
			DDbgPrint("  FileObject == NULL\n");
			Status = STATUS_INVALID_PARAMETER;
			__leave;
		}
        
        //
        //  Mask out any extraneous bits, note that
        //  we can't use the ATTRIBUTE_VALID_FLAGS constant because that has
        //  the control and normal flags set.
        //
        
        FileAttributes   &= (FILE_ATTRIBUTE_READONLY |
                             FILE_ATTRIBUTE_HIDDEN   |
                             FILE_ATTRIBUTE_SYSTEM   |
                             FILE_ATTRIBUTE_ARCHIVE );
                         
		DDbgPrint("  ProcessId %lu\n", IoGetRequestorProcessId(Irp));
        
        // Retrieve the related file's FCB's file name (which is the FULL file name of that object
        RelatedFileFullName = DokanGetRelatedFileFullName(FileObject);
        DDbgPrint("    related file full name: %wZ\n", RelatedFileFullName);
        
        // Check file name for validity
        if (!DokanCheckAndAdjustFileName(&FileObject->FileName, RelatedFileFullName))
        {
            // Not valid
            DDbgPrint("  invalid file name / related file name\n");
			Status = STATUS_OBJECT_NAME_INVALID;
			__leave;
        }
        
        //
        //  Decipher Option flags and values
        //

        //
        //  If this is an open by fileid operation, just fail it explicitly.  FAT's
        //  source of fileids is not reversible for open operations.
        //

        if (BooleanFlagOn( Options, FILE_OPEN_BY_FILE_ID )) {

            DDbgPrint("  open by id not supported\n");
			Status = STATUS_NOT_IMPLEMENTED;
			__leave;
        }

        DirectoryFile           = BooleanFlagOn( Options, FILE_DIRECTORY_FILE );
        NonDirectoryFile        = BooleanFlagOn( Options, FILE_NON_DIRECTORY_FILE );
        SequentialOnly          = BooleanFlagOn( Options, FILE_SEQUENTIAL_ONLY );
        NoIntermediateBuffering = BooleanFlagOn( Options, FILE_NO_INTERMEDIATE_BUFFERING );
        NoEaKnowledge           = BooleanFlagOn( Options, FILE_NO_EA_KNOWLEDGE );
        DeleteOnClose           = BooleanFlagOn( Options, FILE_DELETE_ON_CLOSE );

        TemporaryFile = BooleanFlagOn( IrpSp->Parameters.Create.FileAttributes,
                                       FILE_ATTRIBUTE_TEMPORARY );

        CreateDisposition = (Options >> 24) & 0x000000ff;

        IsPagingFile = BooleanFlagOn( IrpSp->Flags, SL_OPEN_PAGING_FILE );
        OpenTargetDirectory = BooleanFlagOn( IrpSp->Flags, SL_OPEN_TARGET_DIRECTORY );

        CreateDirectory = (BOOLEAN)(DirectoryFile &&
                                    ((CreateDisposition == FILE_CREATE) ||
                                     (CreateDisposition == FILE_OPEN_IF)));

        OpenDirectory   = (BOOLEAN)(DirectoryFile &&
                                    ((CreateDisposition == FILE_OPEN) ||
                                     (CreateDisposition == FILE_OPEN_IF)));
        
        //
        //  Make sure that the dir/nondir indicates a storage type we understand.
        //
        if (DirectoryFile && NonDirectoryFile) {
            DDbgPrint("  requested directory and not-directory!?\n");
			Status = STATUS_INVALID_PARAMETER;
			__leave;
        }
    
        // Is this an open for a root?
        // If so, then validate.
        if (DokanIsRootOpen(FileName, RelatedFileFullName))
        {
            //
            //  Check if we were not supposed to open a directory
            //

            if (NonDirectoryFile) {

                DDbgPrint("  cannot open root directory as a file\n", 0);

                Status = STATUS_FILE_IS_A_DIRECTORY;
                __leave;
            }

            //
            //  Can't open the TargetDirectory of the root directory.
            //

            if (OpenTargetDirectory) {

                DDbgPrint("  cannot open the TargetDirectory of the root directory\n", 0);

                Status = STATUS_INVALID_PARAMETER;
                __leave;
            }

            //
            //  Not allowed to delete root directory.
            //

            if (DeleteOnClose) {

                DDbgPrint("  cannot delete the root directory\n", 0);

                Status = STATUS_CANNOT_DELETE;
                __leave;
            }
        }
        
        /*
        DDbgPrint("File object size: %d", sizeof(FILE_OBJECT));
        DDbgMemory(fileObject, sizeof(FILE_OBJECT));
        
        DDbgPrint("Io stack location size: %d", sizeof(IO_STACK_LOCATION));
        DDbgMemory(irpSp, sizeof(IO_STACK_LOCATION));
        */
        
		Vcb = DeviceObject->DeviceExtension;
		PrintIdType(Vcb);
		if (GetIdentifierType(Vcb) != VCB) {
            DDbgPrint("  invalid vcb\n", 0);
			Status = STATUS_SUCCESS;
			__leave;
		}
        
		Dcb = Vcb->Dcb;

		DDbgPrint("  IrpSp->Flags = %d\n", IrpSp->Flags);
		if (IrpSp->Flags & SL_CASE_SENSITIVE) {
			DDbgPrint("  IrpSp->Flags SL_CASE_SENSITIVE\n");
		}
		if (IrpSp->Flags & SL_FORCE_ACCESS_CHECK) {
			DDbgPrint("  IrpSp->Flags SL_FORCE_ACCESS_CHECK\n");
		}
		if (IrpSp->Flags & SL_OPEN_PAGING_FILE) {
			DDbgPrint("  IrpSp->Flags SL_OPEN_PAGING_FILE\n");
		}
		if (IrpSp->Flags & SL_OPEN_TARGET_DIRECTORY) {
			DDbgPrint("  IrpSp->Flags SL_OPEN_TARGET_DIRECTORY\n");
		}
        if (IrpSp->Flags & SL_OPEN_TARGET_DIRECTORY) {
			DDbgPrint("  IrpSp->Flags SL_OPEN_TARGET_DIRECTORY\n");
		}

		if (RelatedFileObject != NULL) {
			FileObject->Vpb = RelatedFileObject->Vpb;
		} else {
			FileObject->Vpb = Dcb->DeviceObject->Vpb;
		}

		if ((RelatedFileObject == NULL) &&
			FileName.Length == 0) {

			DDbgPrint("   request for FS device\n");

			if (Options & FILE_DIRECTORY_FILE) {
				Status = STATUS_NOT_A_DIRECTORY;
                
			} else {
				SetFileObjectForVCB(FileObject, Vcb);
				Info = FILE_OPENED;
				Status = STATUS_SUCCESS;
			}
			__leave;
		}

        // Don't open file like stream
		if (!Dcb->UseAltStream &&
			DokanUnicodeStringChar(&FileName, L':') != -1) {
			DDbgPrint("    alternate stream\n");
			Status = STATUS_INVALID_PARAMETER;
			Info = 0;
			__leave;
		}

        //
        // Combine the relative file full name with this object's file name
        // This is the actual file / directory that we're opening
        //
        // Overall steps to get correct FileFullName:
        // 1. Compose file name parts.
        // 2. Validate file name parts and state op open.
        // 3. Compose actual combined file name from the parts.
        // 
        // Concrete steps in achieving a correct FileFullName:
        //  
        //  1. Retrieve the relative file full name (this is not just the file name of the relative file object)
        //  2. Validate the file name and the relative file full name (automatically adjusting either one if there is a problem)
        //  3. Validate that the type of open is correct, given the file name / relative full file name.
        //  4. Combine the two correctly.
        //
        FileFullName = DokanCreateFileFullName(FileName, RelatedFileFullName);
        DDbgPrint("    file full name: %wZ\n", FileFullName);

		// This memory is freed by DokanGetFCB if needed
		if (FileFullName.Buffer == NULL) {
			Status = STATUS_INSUFFICIENT_RESOURCES;
			__leave;
		}

		Fcb = DokanGetFCB(Vcb, FileFullName);
		if (Fcb == NULL) {
			Status = STATUS_INSUFFICIENT_RESOURCES;
			__leave;
		}

		if (IrpSp->Flags & SL_OPEN_PAGING_FILE) {
			Fcb->AdvancedFCBHeader.Flags2 |= FSRTL_FLAG2_IS_PAGING_FILE;
			Fcb->AdvancedFCBHeader.Flags2 &= ~FSRTL_FLAG2_SUPPORTS_FILTER_CONTEXTS;
		}

		Ccb = DokanAllocateCCB(Dcb, Fcb);
		if (Ccb == NULL) {
			DokanFreeFCB(Fcb); // FileName is freed here
			Status = STATUS_INSUFFICIENT_RESOURCES;
			__leave;
		}

		FileObject->FsContext = &Fcb->AdvancedFCBHeader;
		FileObject->FsContext2 = Ccb;
        
        // Fake caching initializtion to force Fast I/O
		FileObject->PrivateCacheMap = NULL; // (PVOID)0xFF
        
		FileObject->SectionObjectPointer = &Fcb->SectionObjectPointers;
		//fileObject->Flags |= FILE_NO_INTERMEDIATE_BUFFERING;

		EventLength = sizeof(EVENT_CONTEXT) + Fcb->FileName.Length;
		EventContext = AllocateEventContext(Vcb->Dcb, Irp, EventLength, Ccb);
				
		if (EventContext == NULL) {
			Status = STATUS_INSUFFICIENT_RESOURCES;
			__leave;
		}

		EventContext->Context = 0;
		EventContext->FileFlags |= Fcb->Flags;

		// copy the file name
		EventContext->Create.FileNameLength = FileFullName.Length;
		RtlCopyMemory(EventContext->Create.FileName, Fcb->FileName.Buffer, Fcb->FileName.Length);

		EventContext->Create.FileAttributes = FileAttributes;
		EventContext->Create.CreateOptions  = Options;
		EventContext->Create.DesiredAccess  = *DesiredAccess;
		EventContext->Create.ShareAccess    = ShareAccess;

		// register this IRP to waiting IPR list
		Status = DokanRegisterPendingIrp(DeviceObject, Irp, EventContext, 0);

	} __finally {
    
        /*if (RelatedFileFullName.Buffer != NULL)
        {
            ExFreePool(RelatedFileFullName.Buffer);
        }*/

        DokanPrintNTStatus(Status);
        DDbgPrintExitRequest("DokanCreate", IrpSp);

		if (Status != STATUS_PENDING) {
			Irp->IoStatus.Status = Status;
			Irp->IoStatus.Information = Info;
			IoCompleteRequest(Irp, IO_NO_INCREMENT);
		}

		FsRtlExitFileSystem();
	}

	return Status;
}


VOID
DokanCompleteCreate(
	 __in PIRP_ENTRY			IrpEntry,
	 __in PEVENT_INFORMATION	EventInfo
	 )
{
	PIRP				irp;
	PIO_STACK_LOCATION	irpSp = NULL;
	NTSTATUS			status;
	ULONG				info;
	PDokanCCB			ccb;
	PDokanFCB			fcb;

	irp   = IrpEntry->Irp;
	irpSp = IrpEntry->IrpSp;	

	DDbgPrintEnterComplete("DokanCreate", irpSp);

	ccb	= IrpEntry->FileObject->FsContext2;
	ASSERT(ccb != NULL);
	
	fcb = ccb->Fcb;
	ASSERT(fcb != NULL);

	DDbgPrint("  FileName:%wZ\n", &fcb->FileName);

	ccb->UserContext = EventInfo->Context;
	//DDbgPrint("   set Context %X\n", (ULONG)ccb->UserContext);

	status = EventInfo->Status;

	info = EventInfo->Create.Information;

	switch (info) {
	case FILE_OPENED:
		DDbgPrint("  FILE_OPENED\n");
		break;
	case FILE_CREATED:
		DDbgPrint("  FILE_CREATED\n");
		break;
	case FILE_OVERWRITTEN:
		DDbgPrint("  FILE_OVERWRITTEN\n");
		break;
	case FILE_DOES_NOT_EXIST:
		DDbgPrint("  FILE_DOES_NOT_EXIST\n");
		break;
	case FILE_EXISTS:
		DDbgPrint("  FILE_EXISTS\n");
		break;
	default:
		DDbgPrint("  info = %d\n", info);
		break;
	}

	ExAcquireResourceExclusiveLite(&fcb->Resource, TRUE);
	if (NT_SUCCESS(status) &&
		(irpSp->Parameters.Create.Options & FILE_DIRECTORY_FILE ||
		EventInfo->Create.Flags & DOKAN_FILE_DIRECTORY)) {
		if (irpSp->Parameters.Create.Options & FILE_DIRECTORY_FILE) {
			DDbgPrint("  FILE_DIRECTORY_FILE %p\n", fcb);
		} else {
			DDbgPrint("  DOKAN_FILE_DIRECTORY %p\n", fcb);
		}
		fcb->Flags |= DOKAN_FILE_DIRECTORY;
	}
	ExReleaseResourceLite(&fcb->Resource);

	ExAcquireResourceExclusiveLite(&ccb->Resource, TRUE);
	if (NT_SUCCESS(status)) {
		ccb->Flags |= DOKAN_FILE_OPENED;
	}
    
    /*
    // Process open context
    if (EventInfo->Create.HandleCount > 0)
    {
        // A pointer to the location of the EventInfo->Buffer, as a pointer to a 64-bit ULONG
        PULONG64  PHandle64;
        DWORD     I;
        
        PHandle64 = (PULONG64)(((PCHAR)EventInfo) + FIELD_OFFSET(EVENT_INFORMATION, Buffer));
        
        for (I = 0; I < EventInfo->Create.HandleCount; I++)
        {
            POPEN_CONTEXT_ENTRY OpenContextEntry = ExAllocatePool(sizeof(OPEN_CONTEXT_ENTRY));
            if (OpenContextEntry != NULL)
            {
                PVOID FileObject;
                NTSTATUS    ReferenceStatus;
                
                // Convert the handle stored in the buffer to the appropriate HANDLE type (32-bit vs. 64-bit)
                OpenContextEntry->Handle = (HANDLE)(*(PHandle64 + I));
                
                ReferenceStatus = ObReferenceObjectByHandle(OpenContextEntry->Handle, EventInfo->Create.AccessMask, *IoFileObjectType, UserMode, &FileObject, NULL);
                if (ReferenceStatus != STATUS_SUCCESS)
                {
                    OpenContextEntry->FileObject = NULL;
                    DokanPrintNTStatus(ReferenceStatus);
                    DDbgPrint("   ^ error referencing user handle\n");
                }
                else
                {
                    OpenContextEntry->FileObject = FileObject;
                    DDbgPrint("  OpenContextEntry->Handle %08lx\n", OpenContextEntry->Handle);
                    InsertTailList(&ccb->OpenContext, (PLIST_ENTRY)OpenContextEntry);
                }
            }
        }
    }
    */
    
    DDbgPrint("  %d open contexts\n", EventInfo->Create.HandleCount);
    
	ExReleaseResourceLite(&ccb->Resource);

	if (NT_SUCCESS(status)) {
		if (info == FILE_CREATED) {
			if (fcb->Flags & DOKAN_FILE_DIRECTORY) {
				DokanNotifyReportChange(fcb, FILE_NOTIFY_CHANGE_DIR_NAME, FILE_ACTION_ADDED);
			} else {
				DokanNotifyReportChange(fcb, FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_ADDED);
			}
		}
	} else {
		DDbgPrint("   IRP_MJ_CREATE failed. Free CCB:%X\n", ccb);
		DokanFreeCCB(ccb);
		DokanFreeFCB(fcb);
	}

    DokanPrintNTStatus(status);
    DDbgPrintExitComplete("DokanCreate", irpSp);
	
	irp->IoStatus.Status = status;
	irp->IoStatus.Information = info;
	IoCompleteRequest(irp, IO_NO_INCREMENT);

}